﻿namespace VisualSVN.Core.Licensing
{
    using System;
    using System.IO;
    using System.Security.Cryptography;
    using VisualSVN.Core;
    using VisualSVN.Utils;

    public class RSALicenseCodec : IEncoder, IDecoder
    {
        private const string hname = "MD5";
        private const int KeyLen = 0x400;
        private const int MaxRSABlockSize = 0x56;
        private RSACryptoServiceProvider rsaProvider;

        public RSALicenseCodec(RSACryptoServiceProvider rsaProvider)
        {
            this.rsaProvider = rsaProvider;
        }

        public RSALicenseCodec(RSAParameters rsaParameters)
        {
            this.rsaProvider = this.CreateRSAProvider(rsaParameters);
        }

        private byte[] CloneByteArray(byte[] original)
        {
            if (original == null)
            {
                return null;
            }
            return (byte[]) original.Clone();
        }

        private RSACryptoServiceProvider CreateRSAProvider(RSAParameters rsaParameters)
        {
            RSACryptoServiceProvider provider = new RSACryptoServiceProvider(0x400);
            RSAParameters parameters = this.DeepCopyRSAParameters(rsaParameters);
            provider.ImportParameters(parameters);
            return provider;
        }

        public byte[] Decode(byte[] data)
        {
            if (data.Length < 0x80)
            {
                throw new LicensingException("License key has invalid length.", null);
            }
            byte[] buffer = new byte[data.Length - 0x80];
            using (MemoryStream stream = new MemoryStream(data))
            {
                MemoryStream stream2 = new MemoryStream(buffer);
                HashAlgorithm algorithm = new MD5Managed();
                algorithm.Initialize();
                byte[] buffer2 = new byte[0x80];
                byte[] outputBuffer = new byte[0x56];
                try
                {
                    while (stream.Position < (stream.Length - 0x80L))
                    {
                        int inputCount = stream.Read(buffer2, 0, (int) Math.Min((long) 0x56L, (long) ((stream.Length - 0x80L) - stream.Position)));
                        algorithm.TransformBlock(buffer2, 0, inputCount, outputBuffer, 0);
                        stream2.Write(buffer2, 0, inputCount);
                    }
                    stream.Read(buffer2, 0, 0x80);
                    outputBuffer = new byte[0];
                    algorithm.TransformFinalBlock(outputBuffer, 0, 0);
                    byte[] buffer4 = buffer2;
                    stream.Read(buffer4, 0, buffer4.Length);
                    if (!this.rsaProvider.VerifyHash(algorithm.Hash, "MD5", buffer4))
                    {
                        throw new LicensingException("License key has invalid signature.", null);
                    }
                }
                catch (Exception exception)
                {
                    throw new LicensingException("License decode error (" + exception.Message + ")", exception);
                }
                stream2.Close();
                stream.Close();
            }
            return buffer;
        }

        private RSAParameters DeepCopyRSAParameters(RSAParameters original)
        {
            RSAParameters parameters = new RSAParameters();
            parameters.D = this.CloneByteArray(original.D);
            parameters.DP = this.CloneByteArray(original.DP);
            parameters.DQ = this.CloneByteArray(original.DQ);
            parameters.Exponent = this.CloneByteArray(original.Exponent);
            parameters.InverseQ = this.CloneByteArray(original.InverseQ);
            parameters.Modulus = this.CloneByteArray(original.Modulus);
            parameters.P = this.CloneByteArray(original.P);
            parameters.Q = this.CloneByteArray(original.Q);
            return parameters;
        }

        public byte[] Encode(byte[] data)
        {
            byte[] buffer = new byte[data.Length + 0x80];
            using (MemoryStream stream = new MemoryStream(data))
            {
                int num;
                MemoryStream stream2 = new MemoryStream(buffer);
                byte[] buffer2 = new byte[0x56];
                byte[] outputBuffer = new byte[0x56];
                HashAlgorithm algorithm = new MD5CryptoServiceProvider();
                algorithm.Initialize();
                while ((num = stream.Read(buffer2, 0, 0x56)) == 0x56)
                {
                    algorithm.TransformBlock(buffer2, 0, 0x56, outputBuffer, 0);
                    stream2.Write(buffer2, 0, buffer2.Length);
                }
                buffer2 = algorithm.TransformFinalBlock(buffer2, 0, num);
                stream2.Write(buffer2, 0, buffer2.Length);
                AsymmetricSignatureFormatter formatter = new RSAPKCS1SignatureFormatter(this.rsaProvider);
                formatter.SetHashAlgorithm("MD5");
                outputBuffer = formatter.CreateSignature(algorithm.Hash);
                stream2.Write(outputBuffer, 0, outputBuffer.Length);
                stream2.Close();
                stream.Close();
            }
            return buffer;
        }
    }
}

